package com.cloudlinkscm.base.authcenter.domain;

import com.cloudlinkscm.base.authcenter.assembler.UserCreateDto2UserAccountAssembler;
import com.cloudlinkscm.base.authcenter.constant.AccountStatusEnum;
import com.cloudlinkscm.base.authcenter.dao.*;
import com.cloudlinkscm.base.authcenter.exception.*;
import com.cloudlinkscm.base.authcenter.model.*;
import com.cloudlinkscm.base.authcenter.model.dto.*;
import com.cloudlinkscm.base.authcenter.model.vo.UserRoleRelationDistributionVO;
import com.cloudlinkscm.base.authcenter.model.vo.UserAccountVO;
import com.cloudlinkscm.base.authcenter.model.dto.ResourceTree;
import com.cloudlinkscm.loms.utils.CollectionUtils;
import org.apache.shiro.authc.credential.PasswordService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * @author : tac
 * @date : 28/07/2017
 */
public class AbstractUserAccountManager {
    protected static final int RANDOM_PASSWORD_LENGTH = 6;
    protected static final int RANDOM_PASSWORD_SALT_LENGTH = 3;
    private static final String META_DATA = "0123456789012345678901234567890123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    private UserAccountDao dao;
    private AbstractUserRoleManager userRoleManager;
    private AbstractLoginLogManager loginLogManager;
    private PasswordService passwordService;
    @Autowired
    private RoleDao roleDao;
    @Autowired
    private UserRoleRelationDao userRoleRelationDao;
    @Autowired
    private RolePermissionRelationDao rolePermissionRelationDao;
    @Autowired
    private ResourceManager resourceManager;
    @Autowired
    private TenantResourceDao resourceDao;

    public AbstractUserAccountManager(UserAccountDao dao, AbstractUserRoleManager userRoleManager, PasswordService passwordService) {
        this.dao = dao;
        this.userRoleManager = userRoleManager;
        this.passwordService = passwordService;
    }

    private UserCreateDto2UserAccountAssembler userCreateDto2UserAccountAssembler = new UserCreateDto2UserAccountAssembler();



    /**
     * 创建用户，同时为用户添加默认角色，密码随机生成
     *
     * @return 新创建的用户
     * @see #relateDefaultRoles(UserAccount)
     */
    public UserAccount create(UserAccountCreateDto dto) {

    	UserAccount baseUserAccount = dao.selectByUsername(dto.getUsername());
	    if (baseUserAccount != null) {
			throw new UserAccountUserNameExistedException();
	    }

        int rowNum = 0;

        UserAccount account = userCreateDto2UserAccountAssembler.assemble(dto);
	    String password = randomPassword();

	    account.setPassword(password);
	    account.setUserId(dto.getUserId());

        checkAccountCreateCondition(dto);
        Supplement(account);

        rowNum += dao.insert(account);
        rowNum += relateDefaultRoles(account);
        account.setPassword(password);      //返回密码明文
        return account;
    }

    /**
     * 删除用户，同时删除其所有角色关联关系
     */
    public int delete(String id) {
        int rowNum = 0;
        rowNum += dao.deleteByPrimaryKey(id);
        rowNum += userRoleManager.deleteByUserId(id);
        return rowNum;
    }

    /**
     * 修改密码，需要提供旧密码
     */
    public int changePassword(UserAccountPasswordChangeDto dto) {

        int rowNum = 0;
        String newSalt = randomSalt();
        UserAccount account = dao.selectByPrimaryKey(dto.getUserId());

        // 设置旧密码 和 混淆值
	    dto.setOldPassword(dto.getOldPassword() + account.getSalt());
	    dto.setNewPassword(dto.getNewPassword() + newSalt);

        verifyOldPassword(dto, account);
        rowNum += dao.doChangePassword(account.getId(), dto.getNewPassword());
        return rowNum;
    }

    /**
     * 激活账号
     */
    public int active(UserAccountActiveDto bo) {
    	UserAccount userAccount = dao.selectByPrimaryKey(bo.getUserId());

    	if (userAccount == null) {
			// exception not find
		    throw new UserAccountNotFindException();
	    }

	    if (AccountStatusEnum.STATUS_ACTIVE.code == userAccount.getStatus()) {
    		// is active
		    throw new UserAccountIsActiveException();
	    }

        return dao.active(bo.getUserId());
    }

    /**
     * 启用账号
     */
    public int enable(String id) {
        return dao.enable(id);
    }

    /**
     * 禁用账号
     */
    public int disable(String id){
        return dao.disable(id);
    }

    /**
     * 根据角色id获取与之关联的用户列表
     */
    public List<UserAccount> listByRoleId(String roleId, int pageIndex, int pageSize){
        //todo::
        return new ArrayList<>();
    }

    /**
     * 检查用户创建条件是否满足
     *
     * @see #checkUsernameUniqueConstraint(UserAccountCreateDto)
     */
    protected void checkAccountCreateCondition(UserAccountCreateDto dto) {
        checkUsernameUniqueConstraint(dto);
    }

    protected String randomPassword() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < RANDOM_PASSWORD_LENGTH; i++){
            sb.append(META_DATA.charAt(new Random().nextInt(META_DATA.length())));
        }
        return sb.toString();
    }

	protected String randomSalt() {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < RANDOM_PASSWORD_SALT_LENGTH; i++){
			sb.append(META_DATA.charAt(new Random().nextInt(META_DATA.length())));
		}
		return sb.toString();
	}

    /**
     * 为用户关联默认角色
     */
    protected int relateDefaultRoles(UserAccount account) {
        return userRoleManager.getDao().insertBatch(account.getRoles());
    }

    /**
     * 补全默认用户信息
     */
    protected UserAccount Supplement(UserAccount account) {

    	// 设置 混淆密码 加密
	    String salt = randomSalt();
	    String encryptPassword = account.getPassword() + salt;

//	    String encryptPassword = "hg123456" + salt;
//	    account.setAccount("admin");


	    account.setSalt(salt);
	    account.setPassword(passwordService.encryptPassword(encryptPassword));
        account.setRoles(getDefaultRoles());
        account.setStatus(AccountStatusEnum.STATUS_NOT_ACTIVE.code);
        return account;
    }

    protected Collection<Role> getDefaultRoles() {
        //todo:: 从配置文件或其它地方获取
        return new ArrayList<>();
    }

    /**
     * 校验用户提供的旧密码的正确性
     *
     * @see #verifyPassword(String, String)
     */
    protected void verifyOldPassword(UserAccountPasswordChangeDto bo, UserAccount account) {
        String unencrypted = bo.getOldPassword();
        String encrypted = account.getPassword();
        verifyPassword(unencrypted, encrypted);
    }

    /**
     * 验证密码正确性
     *
     * @param unencrypted 未加密的密码
     * @param encrypted   加密后的密码
     * @throws IncorrectPasswordException 密码不正确
     */
    protected void verifyPassword(String unencrypted, String encrypted) {
        if (!passwordService.passwordsMatch(unencrypted, encrypted)) {
            throw new IncorrectPasswordException();
        }
    }

    /**
     * @throws ExistUsernameException 用户名已存在
     */
    private void checkUsernameUniqueConstraint(UserAccountCreateDto dto) {
        if(dao.isExist(dto.getUsername())){
            throw new ExistUsernameException(dto.getUsername());
        }
    }


    ///
	/// user relation


	public UserAccountVO list(UserAccountSearchDTO searchDTO) {

    	UserAccountVO userAccountVO = dao.page(searchDTO);
		List<UserAccount> userAccounts = userAccountVO.getData();

		Map<String, Role> roleMap = Collections.EMPTY_MAP;
		Map<String, List<UserRoleRelation>> userRoleRelationMultimap = Collections.EMPTY_MAP;
		if (!org.springframework.util.CollectionUtils.isEmpty(userAccounts)) {

			Set<String> accountIds = CollectionUtils.buildSet(userAccounts, String.class, "id");
			List<UserRoleRelation> userRoleRelations = userRoleRelationDao.selectByUserIds(accountIds);

			userRoleRelationMultimap = CollectionUtils.buildMultimap(
					userRoleRelations, String.class, UserRoleRelation.class, "userId");

			if (!org.springframework.util.CollectionUtils.isEmpty(userRoleRelations)) {
				Set<String> roleIds = CollectionUtils.buildSet(userRoleRelations, String.class, "roleId");
				List<Role> roles = roleDao.selectByIds(roleIds);
				roleMap = CollectionUtils.buildMap(roles, String.class, Role.class, "id");
			}
		}

		for (UserAccount userAccount : userAccountVO.getData()) {

			if (userRoleRelationMultimap.containsKey(userAccount.getId())) {
				List<Role> roles = new ArrayList<>();
				Set<String> relationRoleIds = CollectionUtils.buildSet(
						userRoleRelationMultimap.get(userAccount.getId()), String.class, "roleId");

				for (String roleId : relationRoleIds) {
					if (roleMap.containsKey(roleId)) {
						roles.add(roleMap.get(roleId));
					}
				}

				userAccount.setRoles(roles);
			}
		}

		return userAccountVO;
	}

	public UserRoleRelationDistributionVO role(String userId) {

    	if (dao.selectByPrimaryKey(userId) == null) {
    		throw new UserAccountNotFindException();
	    }

		List<UserRoleRelation> userRoleRelations = userRoleRelationDao.selectByUserId(userId);
		List<String> selectedRoleKeys = CollectionUtils.buildList(userRoleRelations, String.class, "roleId");
		List<Role> roles = roleDao.selectAll();
    	return new UserRoleRelationDistributionVO(roles, selectedRoleKeys);
	}

	@Transactional
	public int roleSave(String userId, List<String> roles) {

		if (dao.selectByPrimaryKey(userId) == null) {
			throw new UserAccountNotFindException();
		}

    	if (org.springframework.util.CollectionUtils.isEmpty(roles)) {
    		// empty
	    }
	    userRoleManager.deleteByUserId(userId);

    	int result = 0;
		for (String roleId : roles) {
			UserRoleRelation userRoleRelation = new UserRoleRelation();
			userRoleRelation.setUserId(userId);
			userRoleRelation.setRoleId(roleId);

			result += userRoleRelationDao.insertSelective(userRoleRelation);
		}
    	return result;
	}

	public List<ResourceTree> accountResource(String accountId) {
		List<UserRoleRelation> userRoleRelations = userRoleRelationDao.selectByUserId(accountId);
		if (org.springframework.util.CollectionUtils.isEmpty(userRoleRelations)) {
			return Collections.EMPTY_LIST;
		}

		Set<String> roleIds = CollectionUtils.buildSet(userRoleRelations, String.class, "roleId");
		List<RolePermissionRelation> rolePermissionRelations = rolePermissionRelationDao.selectByRoleIds(roleIds);

		if (org.springframework.util.CollectionUtils.isEmpty(roleIds)) {
			return Collections.EMPTY_LIST;
		}

		List<TenantResource> tenantResources = new ArrayList<>();
		if (!org.springframework.util.CollectionUtils.isEmpty(rolePermissionRelations)) {
			Set<String> resourceIds = CollectionUtils.buildSet(rolePermissionRelations, String.class, "resourceId");
			tenantResources = resourceDao.selectByIds(resourceIds);
		}

		return resourceManager.buildResourceTree(
				resourceManager.convertResource(tenantResources));
	}
}
